package com.computer.fundamentals.algorithm;


import java.util.*;

/**
 * 经典问题——N皇后
 * --------------------------------------------------------------------------------
 * n皇后问题研究的是如何将 n个皇后放置在 n×n 的棋盘上, 并且使皇后彼此之间不能相互攻击。
 * 给你一个整数 n, 返回所有不同的n皇后问题的解决方案。
 * 每一种解法包含一个不同的n 皇后问题 的棋子放置方案, 该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 * |. Q . .|
 * |. . . Q|
 * |Q . . .|
 * |. . Q .|
 *
 * 提示：
 *  1 <= n <= 9
 *  皇后彼此不能相互攻击，也就是说：任何两个皇后都不能处于同一条横行、纵行或斜线上。
 * --------------------------------------------------------------------------------
 */
public class NQueens {

    /**
     * 解决方案：
     *  方案一：回溯
     *      行和列的唯一性十分容易保证, 只需要循环遍历其中一个, 另一个存入Set进行去重即可, 斜对角主要通过：
     *          1. 主对角线 row - col 保持不变
     *          2. 副对角线 row + col 保持不变
 *          通过上述机制我们可以筛选出有一套可行的放置方案, 并递归地求出所有解
     */
    public List<List<String>> solution(int n) {
        List<List<String>> res = new ArrayList<>();
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        Set<Integer> columns = new HashSet<>();
        Set<Integer> diag1 = new HashSet<>();
        Set<Integer> diag2 = new HashSet<>();
        recall(res, queens, n, 0, columns, diag1, diag2);
        return res;
    }

    /**
     * 回溯
     */
    private void recall(
            List<List<String>> res,
            int[] queens,
            int n,
            int row,
            Set<Integer> columns,
            Set<Integer> diag1,
            Set<Integer> diag2
    ) {
        if (row == n) {
            res.add(generate(queens, n));
            return;
        }
        for (int i = 0;i < n;i++) {
            if (columns.contains(i)) {
                continue;
            }
            int d1 = row - i;
            if (diag1.contains(d1)) {
                continue;
            }
            int d2 = row + i;
            if (diag2.contains(d2)) {
                continue;
            }
            queens[row] = i;
            columns.add(i);
            diag1.add(d1);
            diag2.add(d2);
            recall(res, queens, n, row+1, columns, diag1, diag2);
            queens[row] = -1;
            columns.remove(i);
            diag1.remove(d1);
            diag2.remove(d2);
        }
    }

    /**
     * 生成棋盘
     */
    private List<String> generate(int[] queens, int n) {
        List<String> res = new ArrayList<>();
        for (int i = 0;i < n;i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            res.add(new String(row));
        }
        return res;
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        NQueens nQueens = new NQueens();
        System.out.println(nQueens.solution(4));
    }
}
